use std::{collections::HashMap, sync::Arc};

use monitor::{CompleterSuggestion, Monitor, MonitorCommand};

use super::mon_command_completer;
use crate::{
    inode::{inode_file_attr, link_path_walk},
    DfsError, DfsResult,
};

struct LsCommand;

fn is_attr_op(args: &HashMap<String, String>) -> bool {
    let mut is_attr = false;
    let attr = args.get("path");
    if let Some(at) = attr {
        if at == "-l" {
            is_attr = true;
        } else {
            let at = args.get("attr");
            if let Some(a) = at {
                if a == "-l" {
                    is_attr = true;
                }
            }
        }
    }
    is_attr
}

impl LsCommand {
    fn ls_list(&self, args: &HashMap<String, String>, is_attr: bool) -> DfsResult<Vec<String>> {
        let mut v = Vec::new();
        let path = args.get("path");

        let inode;
        if let Some(p) = path {
            if is_attr && p == "-l" {
                inode = link_path_walk(".");
            } else {
                inode = link_path_walk(p);
            }
        } else {
            debug_assert!(!is_attr);
            inode = link_path_walk(".");
        }

        if inode.is_none() {
            return Err(DfsError::NoEnt);
        }
        let inode = inode.unwrap();
        if inode.is_file() {
            if is_attr {
                let at = inode_file_attr(inode);
                v.push(at);
            } else {
                v.push(inode.name().clone());
            }
        } else {
            inode.for_each_childs(|i| {
                if is_attr {
                    let at = inode_file_attr(i.clone());
                    v.push(at);
                } else {
                    v.push(i.name().clone());
                    if i.is_dir() {
                        v.last_mut().unwrap().push('/');
                    }
                }
            });
        }
        v.sort();
        Ok(v)
    }
}

impl MonitorCommand for LsCommand {
    fn mon_protocol_exec(&self, args: &monitor::Protocol) -> DfsResult<monitor::Protocol> {
        let v = self.ls_list(args, is_attr_op(args))?;
        let mut hash = HashMap::new();
        for (i, s) in v.iter().enumerate() {
            hash.insert(i.to_string(), s.clone());
        }
        Ok(hash)
    }

    fn mon_readline_arg_type(&self) -> Option<&'static str> {
        Some("path:*?,attr:*?")
    }

    fn mon_readline_completer(&self, args: &[&str], is_space: bool) -> Option<CompleterSuggestion> {
        mon_command_completer(args.last(), false, is_space)
    }

    fn mon_readline_exec(
        &self,
        args: &HashMap<String, String>,
        printer: &monitor::MonitorPrinter,
    ) -> DfsResult<()> {
        let is_attr = is_attr_op(args);
        let v = self.ls_list(args, is_attr)?;
        if v.is_empty() {
            return Ok(());
        }

        if !is_attr {
            printer.print_term(&v);
        } else {
            let mut ab = String::new();
            for i in v {
                ab.push_str(&i);
                ab.push('\n');
            }
            printer.print(ab);
        }
        Ok(())
    }

    fn mon_readline_help(&self) -> String {
        String::from("列出<文件>的信息")
    }

    fn mon_readline_long_help(&self) -> Option<String> {
        Some(String::from(
            "用法: ls [文件|-l] [-l]

列出文件或者目录的信息(默认为当前目录)
-l 选项: [drwsf] 修改时间 访问时间 文件名",
        ))
    }
}

pub(crate) fn monitor_command_ls_register(mon: &Monitor) {
    mon.register_command("ls", Arc::new(LsCommand)).unwrap();
}
